/*
   Copyright 2007 Abdulla Abdurakhmanov (abdulla@latestbit.com)
   Original sources are available at www.latestbit.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include "cpputils/TestUtils.hpp"
#include "cpputils/utils/OSTools.hpp"
#include "cpputils/utils/OSEndianTools.hpp"
#include "cpputils/utils/SequenceGenerator.hpp"

using namespace cpputils::utils;
using namespace cpputils::exttypes;

BOOST_AUTO_TEST_CASE ( TestHRTime ) {
	hrtime_t nCurrentHrTime = OSTools::gethrtime();
	BOOST_REQUIRE ( nCurrentHrTime!=0 );
	hrtime_t nFreq = OSTools::gethrfreq();
	BOOST_REQUIRE ( nFreq!=0 );
}

BOOST_AUTO_TEST_CASE ( TestSequenceGenerator ) {
	// Create sequence generator for unsigned long (it's default data type)
	SequenceGenerator<> oDefGenerator;
	BOOST_REQUIRE ( oDefGenerator.next() == 1);
	BOOST_REQUIRE ( oDefGenerator++ == 1 );
	BOOST_REQUIRE ( oDefGenerator.current() == 2);
	BOOST_REQUIRE ( ++oDefGenerator == 3);

	// Bounded generator for int type
	SequenceGenerator<int> oBoundedIntGenerator(1,3);
	BOOST_REQUIRE (oBoundedIntGenerator.isBounded());
	BOOST_REQUIRE (oBoundedIntGenerator.current() == 1);
	BOOST_REQUIRE ( ++oBoundedIntGenerator == 2);
	BOOST_REQUIRE ( ++oBoundedIntGenerator == 3);
	BOOST_REQUIRE ( ++oBoundedIntGenerator == 1);
	BOOST_REQUIRE ( oBoundedIntGenerator.next (2)  == 3);
	BOOST_REQUIRE ( --oBoundedIntGenerator == 2);
	BOOST_REQUIRE ( --oBoundedIntGenerator == 1);
	BOOST_REQUIRE ( --oBoundedIntGenerator == 3);

	// Bounded generator for int type by set methods
	SequenceGenerator<int> oBoundedIntGenerator1;
	oBoundedIntGenerator1.setMinValue(1);
	BOOST_REQUIRE (oBoundedIntGenerator1.isBounded());

	// Bounded generator for int type by set methods
	SequenceGenerator<int> oBoundedIntGenerator2;
	oBoundedIntGenerator2.setMaxValue(1);
	BOOST_REQUIRE (oBoundedIntGenerator2.isBounded());
	oBoundedIntGenerator2.setBounded(false);
	BOOST_REQUIRE (!oBoundedIntGenerator2.isBounded());


	// With initial value
	SequenceGenerator<short> oValuedGenerator ( 10 );
	BOOST_REQUIRE (oValuedGenerator.current() == 10);

	SequenceGenerator<short> oBoundedValuedGenerator ( 10, 9, 11 );
	BOOST_REQUIRE (oBoundedValuedGenerator.current() == 10);
	oBoundedValuedGenerator.setCurrentValue ( 11 );
	BOOST_REQUIRE (oBoundedValuedGenerator.current() == 11);
	oBoundedValuedGenerator.setCurrentValue ( 0xFF );
	BOOST_REQUIRE (oBoundedValuedGenerator.current() == 11);
	oBoundedValuedGenerator.setCurrentValue ( 0x0 );
	BOOST_REQUIRE (oBoundedValuedGenerator.current() == 9);

	oBoundedValuedGenerator.setCurrentValue ( 0x0 );
	BOOST_REQUIRE (oBoundedValuedGenerator.current() == 9);

	oBoundedValuedGenerator.setBounded ( false );
	oBoundedValuedGenerator.setCurrentValue ( 22 );
	BOOST_REQUIRE (oBoundedValuedGenerator.current() == 22);
}

BOOST_AUTO_TEST_CASE ( TestProcId ) {
	spawn_pid_t nResult = OSTools::getCurrentProcessId();
	BOOST_REQUIRE ( nResult != 0 );
	thread_id_t hThreadId1 = OSTools::getCurrentThreadId();
	BOOST_REQUIRE ( hThreadId1 != 0 );
	thread_id_t hThreadId2 = OSTools::getCurrentThreadId();
	BOOST_REQUIRE ( OSTools::equalThreadId( hThreadId1, hThreadId2 ) );

}

BOOST_AUTO_TEST_CASE ( TestGetLocale ) {
	BOOST_REQUIRE ( !OSTools::getCurrentLocale().empty() );
}

BOOST_AUTO_TEST_CASE ( TestEndianTools ) {
#ifdef BOOST_BIG_ENDIAN
	BOOST_REQUIRE ( OSEndianTools::isBigEndian() );
	BOOST_REQUIRE ( OSEndianTools::host_to_network(1 ) == 1);
#else
	BOOST_REQUIRE ( OSEndianTools::isLittleEndianArch() );
	BOOST_REQUIRE ( OSEndianTools::host_to_network(1) != 1 );
	BOOST_REQUIRE ( OSEndianTools::host_to_network(1.5f) != 1.5f );
	BOOST_REQUIRE ( OSEndianTools::host_to_network(1.5) != 1.5 );

	int iv = 1;
	BOOST_REQUIRE ( OSEndianTools::host_to_network(iv) != iv );
	boost::int32_t lv = 1;
	BOOST_REQUIRE ( OSEndianTools::host_to_network(lv) != lv );

	time_t tv = time(NULL);
	BOOST_REQUIRE ( (time_t)(OSEndianTools::host_to_network((boost::uint64_t)tv)) != tv );
	BOOST_REQUIRE ( (time_t)(OSEndianTools::network_to_host( OSEndianTools::host_to_network((boost::uint64_t)tv) )) == tv );

#endif
}

class RefObjectTest : public RefObject {
public:
	RefObjectTest() {};
	virtual ~RefObjectTest() {};

	inline virtual void dothrow() throw (const SystemException&) {
		throw SystemException("Hello");
	}

	inline virtual void func1throw() throw (const SystemException&)  {
		dothrow();
	}

	virtual void checkthrow() {
		try {
			func1throw();
		}
		catch(const SystemException& oEx) {
			oEx.printBacktrace();
		}
	}
};

BOOST_AUTO_TEST_CASE ( TestRefObject ) {
	RefObjectTest* a = new RefObjectTest();
	a->addRef();
	a->release();
	a->release();
}

BOOST_AUTO_TEST_CASE ( TestIntrusivePtrRefObject ) {
	boost::intrusive_ptr<RefObjectTest> a ( new RefObjectTest(), false );
}

BOOST_AUTO_TEST_CASE ( TestSystemExceptionBacktrace ) {
	RefObjectTest a;
	try {
		// a.checkthrow();
		a.func1throw();
	}
	catch(const SystemException& oEx) {
#ifdef HAVE_BACKTRACE_SYMBOLS
		BOOST_REQUIRE ( oEx.getBacktrace().size() > 0 );
#else
		std::cout << oEx.what() << std::endl;
#endif
	}
}

